今天主要處理的是 **Context(上下文)**讓它不只是「問一題、回答一題」。
簡單來說,就是讓 AI 記得學生前面講過什麼。這樣學生下一句就算沒有把前面的關鍵字再講一次,AI 也可以根據前面的對話接著回答。
這次主要是在 Node.js 後端處理對話歷史。
原本每次呼叫 API 時,AI 只會看到「這一次」的問題。如果學生第一輪問:
我想學 Python 的 list 觀念。
第二輪只問:
那如果資料不能被修改,我該選哪一個?
如果沒有把前面的對話一起傳進去,AI 其實不知道「哪一個」是在問什麼。
因此這次我把歷史對話一起整理成 Gemini API 可以使用的 contents 格式。
每一輪對話都會記錄使用者和 AI 的內容,例如:
{
role: 'user',
parts: [{ text: '我想學 Python 的 list 觀念。' }]
}
AI 回覆則會記成:
{
role: 'model',
parts: [{ text: '...' }]
}
這樣每次有新的問題,就可以把前面的對話一起送給模型。
在 server.js 裡,我先對前端傳過來的 history 做一次整理。
主要使用 .filter() 和 .map(),把沒有 role、parts 或格式不正確的資料先排除掉,再統一成 Gemini API 可以接受的格式。
這一步看起來可能有點多餘,但如果之後前端傳進來的資料有缺漏,至少不會因為一筆格式錯誤的資料,整個 API 就直接掛掉。
整理完之後,再利用 Spread Operator:
const contents = [
...formattedHistory,
{
role: 'user',
parts: [{ text: message }],
},
];
把過去的對話和這次的新問題放在同一個 contents 裡。
這樣 Gemini 收到的就不只是最新一句,而是可以看到前面的對話脈絡。
為了確認 Context 真的有作用,我在 test-chat.js 裡設計了兩輪測試。
第一輪先問:
我想學 Python 的 list 觀念。
AI 回答之後,把這一輪的 user 和 model 對話存進 history。
接著第二輪故意不再提 list,只問:
那如果資料「不能被修改」,我該選哪一個?
這裡就是這次測試的重點。
如果 AI 有成功讀取前面的 Context,它就應該知道現在還是在討論 Python 的資料結構,進一步帶到 tuple。
這種測試比單純看「AI 有沒有回答成功」更能確認上下文功能到底有沒有真的串起來。
Cannot use import statement outside a module這次實作中間也遇到一個 Node.js 很常見的錯誤:
SyntaxError: Cannot use import statement outside a module
一開始看到這個錯誤會有點莫名其妙,明明 import 的寫法本身沒有問題,為什麼 Node.js 不認?
後來才發現是因為我在 server.js 使用的是 ES Module 的寫法:
import express from 'express';
但 Node.js 預設不一定會把 .js 當成 ES Module 處理。
所以最後直接在 package.json 加上:
{
"type": "module"
}
告訴 Node.js:
這個專案的 JavaScript 使用 ES Module。
加完之後,import 語法就可以正常執行了。
這次也算是再次感受到,實際串 API 的時候,不是只有「程式碼寫對就好」,專案本身的環境設定也會影響程式能不能跑。
這次最大的差別其實不是多了一個 history 變數,而是開始比較能理解 AI 對話記憶到底是怎麼實作的。AI 並不是自己突然「記住」前面講過的東西,而是我們在每次送出新的問題時,把之前的對話整理好,再一起傳給模型,這樣下一次提問時,就可以繼續把前面的內容帶進去。
對 AI 助教來說,這個功能也滿重要的。
因為學生實際問問題時,不太可能每一句都講得很完整。
可能上一句還在問:
list 跟 tuple 有什麼差?
下一句就直接變成:
那不能修改的是哪個?
如果每次都要重新把完整問題講一次,其實滿累的。
有 Context 之後,學生就可以用比較接近平常講話的方式一直追問,AI 再根據前面的內容接著引導。
做到這裡,我覺得 Context 對 AI 助教來說,比單純「回答得很準」還重要。
因為實際學習的過程本來就是連續的。學生可能先搞不懂一個觀念,問了一次之後還是不確定,接著換個方式問;或者只丟一句「那如果不能改呢?」。如果 AI 每次都把它當成全新的問題,學生就會一直重複前面的內容。但如果 AI 能保留對話脈絡,就可以比較接近真正的「一來一往」。
這也讓前面做的蘇格拉底式教學規則開始比較有意義。因為 AI 不只是一直問學生問題,而是可以根據學生前面說過的內容,決定下一步要怎麼引導。
當然,目前這個版本還只是把對話歷史完整帶進去,之後還可以再思考要不要加入更進一步的學生狀態,例如「目前學到哪裡」、「哪個觀念容易搞錯」等等。
今天先把 AI 的 Context 和對話記憶處理好。
明天 Day 09 就要把前幾天做好的東西真正接在一起,進入【前後端串接】。
也就是把今天的 Node.js 後端 API,正式接到之前用 Google Stitch 做好的前端介面。
希望明天做完之後,就不只是從終端機測試,而是可以直接在網頁上跟自己的 AI 助教對話。